/* **********************************************************************
版本: 2022.3.62f1
作者: wzz
日期: 2025-8-7
描述: 固定包头适配器， 解决：粘包、分包;
      特别注意：需要和服务端确认《包头大小》与《端序》。
***********************************************************************/

using System;
using System.Buffers.Binary;
using System.IO;

public class FixDataAdpter
{
    /// <summary>
    /// 固定包头类型
    /// </summary>
    public enum FixedHeaderType : byte
    {
        None = 0,  // 表示非固定包头，直接返回原始数据
        Byte = 1,  // 1字节
        Ushort = 2,// 2字节 最大接收：63k
        Int = 4    // 4字节 最大接收：2G
    }

    /// <summary>
    /// 包头所占的字节数
    /// </summary>
    public int HeadSize
    { get { return mHeadSize; } }

    /// <summary>
    /// 一次性从接收缓冲区读取的数据，默认20k，缓冲区的数据大于此值会分多次读取
    /// </summary>
    public byte[] Buffer
    { get { return mBuffer; } }

    /// <summary>
    /// 游戏开发，接收数据一般不会超过20k，基本1k以内，即使slg类的战报，大的通常也只有100k左右
    /// 但 包头为int时，最大数据为2G。此值检验大小端设置错误以及其它情况数据异常过大。
    /// </summary>
    public int mBufferMaxSize { get; set; } = 1024 * 200;

    /// <summary>
    /// 解析包成功回调
    /// </summary>
    public Action<byte[]> OnParseSuccessCallback;

    /// <summary>
    /// 服务端序为大端(默认true)
    /// </summary>
    public bool ServerIsBigEndian { get; set; } = true;

    /// <summary>
    /// 缓存超时时间。默认1秒。
    /// </summary>
    public TimeSpan CacheTimeout { get; set; } = TimeSpan.FromSeconds(1);

    /// <summary>
    /// 是否启用缓存超时。默认false。在极小非正常情况下，包头与长度没有对应上，开启可纠正数据
    /// </summary>
    public bool CacheTimeoutEnable { get; set; } = true;

    private DateTimeOffset LastCacheTime { get; set; } = DateTimeOffset.UtcNow;
    private FixedHeaderType mFixedHeaderType = FixedHeaderType.Ushort;
    private byte mHeadSize = 0;
    private byte[] mBuffer;
    private byte[] mHeadBuffer;
    private MemoryStream mMemoryStream;
    private BinaryReader mBinaryReader;

    public FixDataAdpter(FixedHeaderType fixedHeaderType = FixedHeaderType.Ushort, int maxBufferSize = 1024 * 20)
    {
        mFixedHeaderType = fixedHeaderType;
        mHeadSize = (byte)fixedHeaderType;

        mBuffer = new byte[maxBufferSize];
        mHeadBuffer = new byte[mHeadSize];
        mMemoryStream = new MemoryStream();
        mBinaryReader = new BinaryReader(mMemoryStream);
    }

    /// <summary>
    /// 清空buffer
    /// </summary>
    public void ClearBuffer()
    {
        Array.Clear(mBuffer, 0, mBuffer.Length);
    }

    /// <summary>
    /// 解包成功
    /// </summary>
    /// <param name="data"></param>
    private void OnParseSuccess(byte[] data)
    {
        OnParseSuccessCallback?.Invoke(data);
    }

    public int index = 0;

    /// <summary>
    /// 解包
    /// </summary>
    public void SplitPackage(int readBytes)
    {
        if (mFixedHeaderType == FixedHeaderType.None)
        {
            byte[] data = new byte[readBytes];
            Array.Copy(mBuffer, data, readBytes);
            OnParseSuccess(data);
            return;
        }

        if (CacheTimeoutEnable && DateTimeOffset.UtcNow - LastCacheTime > CacheTimeout)
        {
            mMemoryStream.SetLength(0);
            mMemoryStream.Write(mBuffer, 0, readBytes);
            mMemoryStream.Seek(0, SeekOrigin.Begin);
        }
        else
        {
            mMemoryStream.Seek(0, SeekOrigin.End);
            mMemoryStream.Write(mBuffer, 0, readBytes);
            mMemoryStream.Seek(0, SeekOrigin.Begin);
        }

        if (CacheTimeoutEnable)
            LastCacheTime = DateTimeOffset.UtcNow;

        while (RemainingBytes() > HeadSize)
        {
            int dataLen = GetDataLen();
            if (dataLen > mBufferMaxSize)
            {
                ReSet();
                throw new HeadFlagLargeException($"收到的数据byte:{readBytes}，头标识的数据byte:{dataLen}， 自定义最大数据byte:{mBufferMaxSize}");
            }

            if (RemainingBytes() >= dataLen)
            {
                OnParseSuccess(mBinaryReader.ReadBytes(dataLen));
            }
            else
            {
                mMemoryStream.Position -= HeadSize;
                break;
            }
        }

        // 把剩下的字节，写入缓存
        byte[] leftOver = mBinaryReader.ReadBytes(RemainingBytes());
        mMemoryStream.SetLength(0);
        mMemoryStream.Write(leftOver, 0, leftOver.Length);
    }

    /// <summary>
    /// 剩下要处理的的字节数
    /// </summary>
    private int RemainingBytes()
    {
        return (int)(mMemoryStream.Length - mMemoryStream.Position);
    }

    /// <summary>
    /// 在数据中写入头字节
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public byte[] WriteHeaderInBytes(byte[] data)
    {
        if (mFixedHeaderType == FixedHeaderType.None)
        {
            return data;
        }

        var len = data.Length;
        var headbytes = GetHeadBuffer(len);

        var newData = new byte[len + HeadSize];
        Array.Copy(headbytes, newData, headbytes.Length);
        Array.Copy(data, 0, newData, headbytes.Length, data.Length);

        return newData;
    }

    /// <summary>
    /// 获取头部字节
    /// </summary>
    public byte[] GetHeadBuffer(int dataLen)
    {
        Array.Clear(mHeadBuffer, 0, mHeadBuffer.Length);

        switch (mFixedHeaderType)
        {
            case FixedHeaderType.Ushort:
                if (ServerIsBigEndian)
                    BinaryPrimitives.WriteUInt16BigEndian(mHeadBuffer, (ushort)dataLen);
                else
                    BinaryPrimitives.WriteUInt16LittleEndian(mHeadBuffer, (ushort)dataLen);
                break;

            case FixedHeaderType.Int:
                if (ServerIsBigEndian)
                    BinaryPrimitives.WriteInt32BigEndian(mHeadBuffer, dataLen);
                else
                    BinaryPrimitives.WriteInt32LittleEndian(mHeadBuffer, dataLen);
                break;

            default:
                mHeadBuffer[0] = (byte)dataLen;
                break;
        }

        return mHeadBuffer;
    }

    /// <summary>
    /// 重置
    /// </summary>
    public void ReSet()
    {
        ClearBuffer();
        mMemoryStream.SetLength(0);
        mMemoryStream.Seek(0, SeekOrigin.Begin);
    }

    /// <summary>
    /// 获取当前数据的长度
    /// </summary>

    public int GetDataLen()
    {
        int len = 0;
        switch (mFixedHeaderType)
        {
            case FixedHeaderType.Ushort:
                if (ServerIsBigEndian)
                {
                    if (BitConverter.IsLittleEndian)

                        len = BinaryPrimitives.ReverseEndianness(mBinaryReader.ReadUInt16());
                    else
                        len = mBinaryReader.ReadUInt16();
                }
                else
                {
                    if (!BitConverter.IsLittleEndian)
                        len = BinaryPrimitives.ReverseEndianness(mBinaryReader.ReadUInt16());
                    else
                        len = mBinaryReader.ReadUInt16();
                }
                break;

            case FixedHeaderType.Int:
                if (ServerIsBigEndian)
                {
                    if (BitConverter.IsLittleEndian)
                        len = BinaryPrimitives.ReverseEndianness(mBinaryReader.ReadInt32());
                    else
                        len = mBinaryReader.ReadInt32();
                }
                else
                {
                    if (!BitConverter.IsLittleEndian)
                        len = BinaryPrimitives.ReverseEndianness(mBinaryReader.ReadInt32());
                    else
                        len = mBinaryReader.ReadInt32();
                }
                break;

            default:
                len = mBinaryReader.ReadByte();
                break;
        }

        return len;
    }

    public class HeadFlagLargeException : Exception
    {
        public HeadFlagLargeException(string message) : base(message)
        {
        }
    }
}